package ucalgary.seng401.tss;


import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.text.DecimalFormat;
import java.util.Calendar;
import java.util.Date;
import javax.swing.Timer;

import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.JToggleButton;

public class TSSSimulator2 extends JFrame implements ActionListener {
	
	private static final int NUM_CALL_RECORDS = 16;
	private static final int NUM_ROUTES = 5;
	
	private final JToggleButton[] phoneButton;
	private JToggleButton[] routeLabel;
	private JButton loInterruptButton;
	private JButton hiInterruptButton;
	private JButton clockButton; 
	private JLabel[] statusLabel, processLabel, calledNumLabel, routeNumLabel, timeLabel, callingTerminationLabel, calledTerminationLabel;
	private JLabel[] routeStatusLabel, routeCallingNum, routeCalledNum;
	public Timer[] timers;
	private int phoneToConnect1 = -1;
	private int phoneToConnect2 = -1;
	private static final Color[] listOfColors = { Color.RED, Color.BLUE, Color.GREEN, Color.YELLOW, Color.orange };
	//Array representing the 5 available routes. 1 = in use, 0 = available.
	private int[] numConnections = new int[5];
	private JTextField hh, mm, ss;
	
	private TSSArchitecture arc; 
	
	private int flashing  = -1;
	
	private Timer t;
	private Timer t2;
	
	private boolean testClockEnabled = false; 
	
	public TSSSimulator2() {
		
		super("TSS Simulator");
		arc = new TSSArchitecture(); 
		timers = new Timer[NUM_CALL_RECORDS];
		Container container = this.getContentPane();
		container.setLayout( new BorderLayout() );
		container.setBackground( Color.WHITE );
		
		JPanel topPanel = new JPanel();
		topPanel.setLayout( new BoxLayout(topPanel, BoxLayout.X_AXIS) );
		
		JPanel callRecordPanel = new JPanel( new GridLayout(NUM_CALL_RECORDS,8) );
		callRecordPanel.setBackground( Color.WHITE );
		callRecordPanel.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(10,10,10,10), BorderFactory.createTitledBorder("Call Records") ) );
		
		phoneButton = new JToggleButton[NUM_CALL_RECORDS];
		statusLabel = new JLabel[NUM_CALL_RECORDS];
		processLabel = new JLabel[NUM_CALL_RECORDS];
		calledNumLabel = new JLabel[NUM_CALL_RECORDS];
		routeNumLabel = new JLabel[NUM_CALL_RECORDS];
		timeLabel = new JLabel[NUM_CALL_RECORDS];
		callingTerminationLabel = new JLabel[NUM_CALL_RECORDS];
		calledTerminationLabel = new JLabel[NUM_CALL_RECORDS];
		
		for( int i = 0; i < NUM_CALL_RECORDS; i++ ) {
			phoneButton[ i ] = new JToggleButton( "Phone(" + i + ")" );
			phoneButton[ i ].addActionListener( this );
			phoneButton[ i ].setForeground(Color.BLACK);
			callRecordPanel.add( phoneButton[ i ] );
			statusLabel[ i ] = new JLabel( "Status" );
			callRecordPanel.add( statusLabel[ i ] );
			processLabel[ i ] = new JLabel( "Idle" );
			callRecordPanel.add( processLabel[ i ] );
			calledNumLabel[ i ] = new JLabel( "CalledNum" );
			callRecordPanel.add( calledNumLabel[ i ] );
			routeNumLabel[ i ] = new JLabel( "RouteNum" );
			callRecordPanel.add( routeNumLabel[ i ] );
			timeLabel[ i ] = new JLabel ( "Time" );
			callRecordPanel.add( timeLabel[ i ] );
			callingTerminationLabel[ i ] = new JLabel( "CallingTerm" );
			callRecordPanel.add( callingTerminationLabel[ i ] );
			calledTerminationLabel[ i ] = new JLabel( "CalledTerm" );
			callRecordPanel.add( calledTerminationLabel[ i ] );
		}
		
		JPanel routePanel = new JPanel( new GridLayout(NUM_ROUTES, 4) );
		routePanel.setBackground( Color.WHITE );
		routePanel.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(10,10,10,10), BorderFactory.createTitledBorder("Routes") ) );
		
		routeLabel = new JToggleButton[ NUM_ROUTES ];
		routeStatusLabel = new JLabel[ NUM_ROUTES ];
		routeCallingNum = new JLabel[ NUM_ROUTES ];
		routeCalledNum = new JLabel[ NUM_ROUTES ];
				
		for ( int i = 0; i < NUM_ROUTES; i++ ) {
			routeLabel[i] = new JToggleButton("Route(" + i + ")");
			routeLabel[i].addActionListener(this);
			routeLabel[i].setForeground(Color.BLACK);
			routeLabel[i].setEnabled(false);
			routePanel.add( routeLabel[i] );
			routeStatusLabel[i] = new JLabel("Status");
			routePanel.add( routeStatusLabel[i] );
			routeCallingNum[i] = new JLabel("CallingNum");
			routePanel.add( routeCallingNum[i] );
			routeCalledNum[i] = new JLabel("CalledNum");
			routePanel.add( routeCalledNum[i] );
		}
		
		callRecordPanel.setMaximumSize( new Dimension(800,550) );
		
		topPanel.add( callRecordPanel );
		topPanel.add( routePanel );
		
		JPanel bottomPanel = new JPanel();
		bottomPanel.setLayout( new BoxLayout(bottomPanel, BoxLayout.X_AXIS) );
		bottomPanel.setBackground( new Color(20, 120, 200) );
		
		Date currentTime = Calendar.getInstance().getTime();
		
		hh = new JTextField(2);
		bottomPanel.add( hh );
		hh.setEditable( false );
		hh.setMaximumSize( new Dimension(30,25) );
		hh.setAlignmentX( CENTER_ALIGNMENT );
		hh.setText( String.valueOf( currentTime.getHours() ) );
		
		JLabel colon = new JLabel(" : ");
		bottomPanel.add( colon );
		
		mm = new JTextField(2);
		bottomPanel.add( mm );
		mm.setEditable( false );
		mm.setMaximumSize( new Dimension(30,25) );
		mm.setAlignmentX( CENTER_ALIGNMENT );
		mm.setText( String.valueOf( currentTime.getMinutes() ) );
		
		JLabel colon2 = new JLabel(" : ");
		bottomPanel.add( colon2 );
		
		ss = new JTextField(2);
		bottomPanel.add( ss );
		ss.setEditable( false );
		ss.setMaximumSize( new Dimension(30,25) );
		ss.setAlignmentX( CENTER_ALIGNMENT );
		ss.setText( String.valueOf( currentTime.getSeconds() ) );
		
		loInterruptButton = new JButton("L_INT");
		bottomPanel.add( loInterruptButton );
		loInterruptButton.addActionListener(this); 
		loInterruptButton.setAlignmentX( CENTER_ALIGNMENT );
		hiInterruptButton = new JButton("H_INT");
		hiInterruptButton.addActionListener(this); 
		bottomPanel.add( hiInterruptButton );
		hiInterruptButton.setAlignmentX( CENTER_ALIGNMENT );
		clockButton = new JButton("Clock");
		bottomPanel.add( clockButton );
		clockButton.setAlignmentX( CENTER_ALIGNMENT );
		clockButton.addActionListener(this); 
		
		this.add( topPanel, BorderLayout.NORTH );
		this.add( bottomPanel, BorderLayout.SOUTH );
		
		t = new Timer(1000, new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				if (!testClockEnabled) { 
					arc.getSysClock().updateCurrentTime();
				} 

				DecimalFormat twoDigits = new DecimalFormat("00");
				hh.setText(String.valueOf(twoDigits.format(arc.getSysClock().getCurrentTime().getHours())));
				mm.setText(String.valueOf(twoDigits.format(arc.getSysClock().getCurrentTime().getMinutes()))); 
				ss.setText(String.valueOf(twoDigits.format(arc.getSysClock().getCurrentTime().getSeconds()))); 

			}
		});
		
		t.start();
		
		this.setSize( 1300,600 );
		this.setVisible( true );
			
	}
	
	public void actionPerformed( ActionEvent evt ) {

		//Toggle button pressed.
		if ( evt.getSource() instanceof JToggleButton ) {
			String buttonText = ((JToggleButton)evt.getSource()).getText();
			//If the button pressed was one of the phone buttons.
			if (buttonText.contains("Phone")){
				final int phoneClicked = Integer.parseInt( buttonText.substring(buttonText.indexOf('(') + 1, buttonText.indexOf(')') ) );
				//If the pressed phone button is not already in a call process it
				if (phoneButton[ phoneClicked ].getForeground() == Color.BLACK || phoneClicked == flashing){
					
					if ( phoneToConnect1 == -1 ) {
										
						phoneToConnect1 = Integer.parseInt( buttonText.substring(buttonText.indexOf('(') + 1, buttonText.indexOf(')') ) );
						//phoneButton[ phoneToConnect1 ].setEnabled( false );
						int useLine = 0;
						for (int i = 0; i < 5; i++){
							if (numConnections[i] == 0){
								useLine = i;
								//numConnections[i] = 1;
								break; //Found available route stop looking.
							}
						}
						phoneButton[ phoneToConnect1 ].setForeground( listOfColors[ useLine ] );
						setLabelDialing(phoneToConnect1);
						
						
						///////KEYPAD STUFFF//////////////
						final NumberPad keypad = new NumberPad();
						keypad.setVisible(true);
						for( int i = 0; i < keypad.keys.length; i++ ) {
							keypad.keys[i].addActionListener( new ActionListener() {
								public void actionPerformed( ActionEvent e ) {
									int digit = Integer.parseInt( ((JButton)e.getSource()).getText() );
									if ( keypad.getNumDigitsPressed() == 0 ) {
										keypad.number = digit;
										keypad.numDigitsPressed++;
									}
									else {
										String t = keypad.number + "" + digit;
										keypad.setNumber( Integer.parseInt(t) );
										keypad.setVisible(false);
										System.out.println( keypad.getNumber() );
										
										
										flashing = keypad.getNumber();
										
										t2 = new Timer(400, new ActionListener() {
											public void actionPerformed(ActionEvent e) {
												
												int useLine = -1;
												for (int i = 0; i < 5; i++){
													if (numConnections[i] == 0){
														useLine = i;
														break; //Found available route stop looking.
													}
												}
												if ( phoneButton[keypad.getNumber()].getForeground().equals(Color.BLACK) ) {
													phoneButton[keypad.getNumber()].setForeground(listOfColors[useLine]);
												}
												//else if ( phoneButton[keypad.getNumber()].getForeground().equals(phoneButton[phoneClicked].getForeground() ) ) {
												else {	
													phoneButton[keypad.getNumber()].setForeground(Color.BLACK);
												}
											}
										});
										
										t2.start();
										
									}
								}
							});
						}
					}
					else if ( phoneToConnect2 == -1 ) {
						
							phoneToConnect2 = Integer.parseInt( buttonText.substring(buttonText.indexOf('(') + 1, buttonText.indexOf(')') ) );

							if ( phoneToConnect2 == flashing ) {
								System.out.println("Stop flashing");
								t2.stop(); 
								flashing = -1;
								
							}
							else if (flashing != -1) {
								
								return;
							}
							
							phoneButton[ phoneToConnect1 ].setEnabled( true );
							//Find the first available connection
							int useLine = -1;
							for (int i = 0; i < 5; i++){
								if (numConnections[i] == 0){
									useLine = i;
									numConnections[i] = 1;
									break; //Found available route stop looking.
								}
							}
							//All routes are busy
							if (useLine == -1){
								System.out.println("NO ROUTES AVAILABLE");
								phoneButton[ phoneToConnect1 ].setSelected( false );
								phoneButton[ phoneToConnect2 ].setSelected( false );
								phoneButton[ phoneToConnect1 ].setEnabled( true );
								phoneButton[ phoneToConnect2 ].setEnabled( true );
								setLabelBusyRoutes(phoneToConnect1);
								phoneToConnect1 = -1;
								phoneToConnect2 = -1;
							}else{
								phoneButton[ phoneToConnect1 ].setForeground( listOfColors[ useLine ] );
								phoneButton[ phoneToConnect2 ].setForeground( listOfColors[ useLine ] );
								routeLabel[ useLine ].setSelected(false);
								routeLabel[ useLine ].setEnabled( true );
								routeLabel[ useLine ].setForeground( listOfColors[ useLine ] );
								setLabelTalking(phoneToConnect1,phoneToConnect2,useLine);
								
								//start the timers and update timer labels
								Timer t1 = new Timer(1000, new ActionListener() {
									Date elapsedTime = null;
									int calling = phoneToConnect1;
									int called = phoneToConnect2;
									DecimalFormat twoDigits = new DecimalFormat("00");
									Date startTime = arc.getSysClock().getCurrentTime();
									public void actionPerformed(ActionEvent e) {
										if (!testClockEnabled) { 
											arc.getSysClock().updateCurrentTime();
										} 
										long elapsedTime = arc.getSysClock().getCurrentTime().getTime()- startTime.getTime();
										long hours = elapsedTime / (60*60000);
										long minutes = (elapsedTime%(60*60000)) / 60000;
										long seconds = ((elapsedTime%(60*60000)) % 60000) / 1000;
										timeLabel[calling].setText((twoDigits.format(hours)) +":" + 
															twoDigits.format(minutes)+":" +
															twoDigits.format(seconds));
										timeLabel[called].setText(twoDigits.format(hours) +":" + 
												twoDigits.format(minutes)+":" +
												twoDigits.format(seconds));
									}
								});
								resetTerminationLabels(phoneToConnect1,phoneToConnect2);
								timers[phoneToConnect1] = timers[phoneToConnect2] = t1;
								t1.start();
								phoneToConnect1 = -1;
								phoneToConnect2 = -1;
							}
					}
				//Else if the second pressed phone button is already in a call give busy tone.
				}else if (phoneButton[ phoneClicked ].getForeground() != Color.BLACK && phoneToConnect1 != -1){
					System.out.println("BUSY TONE");
					phoneButton[ phoneToConnect1 ].setSelected( false );
					phoneButton[ phoneClicked ].setSelected( true );
					phoneButton[ phoneToConnect1 ].setEnabled( true );
					phoneButton[ phoneClicked ].setEnabled( true );
					setLabelBusyTone(phoneToConnect1);
					phoneToConnect1 = -1;
					phoneToConnect2 = -1;
				//Else if the first pressed phone button is already in a call hang up.
				}else if (phoneButton[ phoneClicked ].getForeground() != Color.BLACK && phoneToConnect1 == -1){
					Color currColour = phoneButton[ phoneClicked ].getForeground();
					resetPhone(phoneClicked);
					callingTerminationLabel[phoneClicked].setText("True");
					//stop timer for that call and reset label
					timers[phoneClicked].stop();
					timeLabel[phoneClicked].setText("Time");

					//Small delay to simulate
					
					//Find the other phone button in this call and color it black too
				
					for (int i = 0; i < 15; i++){
						if (phoneButton[ i ].getForeground() == currColour && i !=phoneClicked){
							calledTerminationLabel[i].setText("True");
							Wait.oneSec();
							resetPhone(i);
							//Stop timer for that call and reset label.
							timers[i].stop();
							timeLabel[i].setText("Time");
						}
					}
					
					//Find the route button in this call and color it black too
					for (int i = 0; i < 5; i++){
						if (routeLabel[ i ].getForeground() == currColour){
							routeLabel[ i ].setForeground(Color.BLACK);
							routeLabel[i].setEnabled(false);
							setRouteLabelsHangup(i);
							numConnections[i] = 0;
							break;
						}
					}
					
				}
				
			//If the button pressed was one of the route buttons.	
			}else if (buttonText.contains("Route")){
				int routeNumber = Integer.parseInt( buttonText.substring(buttonText.indexOf('(') + 1, buttonText.indexOf(')') ) );
				if (routeLabel[ routeNumber ].getForeground() != Color.BLACK){
					//Hang up the two phones
					//Find the phone buttons associated with the route and color them black.
					Color currColour = routeLabel[ routeNumber ].getForeground();
					for (int i = 0; i < 15; i++){
						if (phoneButton[ i ].getForeground() == currColour){	
							//Stop timer for that call
							timers[i].stop();
							timeLabel[i].setText("Time");
							resetPhone(i);
							//reset termination labels
							callingTerminationLabel[i].setText("CallingNum");
							calledTerminationLabel[i].setText("CalledNUm");
							Wait.oneSec();
						}
					}
					//Re-colour the route
					routeLabel[ routeNumber ].setForeground(Color.BLACK);
					routeLabel[ routeNumber ].setEnabled(false);
					routeLabel[ routeNumber ].setSelected(false);
					//Free up the route in the route array.
					numConnections[routeNumber] = 0;
					setRouteLabelsHangup(routeNumber);
				}
			}
		}
		if (evt.getSource().equals(loInterruptButton)) { 
			arc.L3InterruptPC(); 
			System.out.println("L3_INTR"); 
			System.out.println("  Running LineScanningPC");
		}
		if (evt.getSource().equals(hiInterruptButton)) { 
			arc.L4InterruptPC(); 
			System.out.println("L4_INTR"); 
			System.out.println("  Running SysClockPC");
			System.out.println("  Running DigitsReceivingPC");
			if (phoneToConnect1 != -1)  
				System.out.println("" + "    DigitsReceiver.Digit1: " + phoneToConnect1);
		}
		if (evt.getSource().equals(clockButton)) {
			System.out.println("CLOCK Simulation");
			System.out.println("  Setting time to 23:59:55");
			testClockEnabled = true; 
			arc.getSysClock().setCurrentTime(23, 59, 55); 
			t.stop();
			t = new Timer(1000, new ActionListener() {
				public void actionPerformed(ActionEvent e) {
					arc.getSysClock().setInterruptCounter(999); 
					arc.getSysClock().setCurrentTime(arc.getSysClock().getCurrentTime().getHours(), arc.getSysClock().getCurrentTime().getMinutes(),arc.getSysClock().getCurrentTime().getSeconds() + 1 );
					hh.setText(String.valueOf(arc.getSysClock().getCurrentTime().getHours()));
					mm.setText(String.valueOf(arc.getSysClock().getCurrentTime().getMinutes())); 
					ss.setText(String.valueOf(arc.getSysClock().getCurrentTime().getSeconds())); 
				}
			
			}); 
			t.start(); 
			arc.SysClockPC(); 
			
		}
	}
	
	
	private void resetPhone(int i) {
		phoneButton[ i ].setForeground(Color.BLACK);
		phoneButton[ i ].setSelected(false);
		setProcessLabelsHangup(i);
		
	}

	//This method sets the label to say "Busy Line" when a phone calls another phone that is already in a call.
	private void setLabelBusyTone(int phoneNum) {
		processLabel[phoneNum].setText("Busy Line");
		calledNumLabel[phoneNum].setText("Called Num");
		
	}

	//This method sets the label to say "No Route" when a phone calls another phone when there are not routes available.
	private void setLabelBusyRoutes(int phoneNum) {
		processLabel[phoneNum].setText("No Route");
		calledNumLabel[phoneNum].setText("Called Num");
	}

	public static void main( String[] args ) {
		TSSSimulator2 ts = new TSSSimulator2();
		ts.addWindowListener( 
			new WindowAdapter() {
				public void windowClosing( WindowEvent e ) {
					System.exit(0);
				}
			} 
		);
	}
	
	public void setLabelDialing(int phoneNum)
	{
		processLabel[phoneNum].setText("Dialing");
		calledNumLabel[phoneNum].setText(Integer.toString(phoneNum));
	}
	
	public void setLabelTalking(int callingNum, int calledNum,int routeNum)
	{
		processLabel[callingNum].setText("Talking");
		processLabel[calledNum].setText("Talking");
		calledNumLabel[calledNum].setText(Integer.toString(callingNum));
		calledNumLabel[callingNum].setText(Integer.toString(calledNum));
		routeNumLabel[callingNum].setText(Integer.toString(routeNum));
		routeNumLabel[calledNum].setText(Integer.toString(routeNum));
		routeStatusLabel[routeNum].setText("Talking");
		routeCallingNum[routeNum].setText(Integer.toString(callingNum));
		routeCalledNum[routeNum].setText(Integer.toString(calledNum));
	}
	
	public void setProcessLabelsHangup(int phoneNum)
	{
		processLabel[phoneNum].setText("Idle");
		calledNumLabel[phoneNum].setText("Called Num");
		routeNumLabel[phoneNum].setText("RouteNum");
	}
	
	public void setRouteLabelsHangup(int routeNum)
	{
		routeStatusLabel[routeNum].setText("Status");
		routeCallingNum[routeNum].setText("CallingNum");
		routeCalledNum[routeNum].setText("CalledNum");
	}
	
	public void resetTerminationLabels(int calling, int called)
	{
		callingTerminationLabel[calling].setText("CallingTerm");
		callingTerminationLabel[called].setText("CallingTerm");
		calledTerminationLabel[calling].setText("CalledTerm");
		calledTerminationLabel[called].setText("CalledTerm");
	}
}